import Lexer, { type LexerConfig } from "./lexer";
import { LexerToken, LexerTokenType } from "./lexerToken";
import Parser from "./parser";

export default class Ast {
  lexer?: Lexer;
  parser?: Parser;
  constructor(config?: LexerConfig) {
    this.lexer = new Lexer(config);
    this.parser = new Parser();
  }

  async gen(str?: string) {
    try {
      if (!str)
        return Promise.reject({
          status: "error",
          message: "formula is empty",
        });
      const tokens = await this.getLexer(str);
      console.log("debug-> ~ Ast ~ gen ~ tokens:", tokens);
      const result = await this.getParser(tokens);
      console.log("debug-> ~ Ast ~ gen ~ result:", result);
      return result;
    } catch (error) {
      console.log("debug-> ~ Ast ~ gen ~ error:", error);
    }
  }

  async getLexer(str: string) {
    let tokens: LexerToken[] = [];
    let chars = str.split("");
    for (let i = 0; i < chars.length; i++) {
      try {
        const charToken = this.lexer!.push(chars[i], i);
        tokens = [...tokens, ...charToken];
      } catch (error) {
        const start = this.lexer?.startAt || 0;
        this.lexer?.clear();
        return Promise.reject({
          status: "error",
          message: error,
          start: start + 1,
          end: i,
        });
      }
    }
    tokens = [...tokens, ...this.lexer!.end()];
    this.lexer?.clear();
    return tokens;
  }

  async getParser(tokens: LexerToken[]) {
    let start = 0,
      end = 0;
    for (let i = 0; i < tokens.length; i++) {
      const token = tokens[i];
      try {
        end = start + token.value.length;
        this.parser?.push(token);
        start += token.value.length;
        if (
          token.type === LexerTokenType.FUNCTION_END ||
          token.type === LexerTokenType.DATA
        ) {
          start++;
        }
      } catch (error) {
        return Promise.reject({
          status: "error",
          message: error,
          start,
        });
      }
    }
    return Promise.resolve(this.parser?.end());
  }
}
